/*
This is a sample program to implement a client, which connects to a server
on a given port, sends a filename and then saves the contents of the response
as a file.

Usage: client <hostname> <port>

Author: Akos Fabian
Date: 2012.05.03

*/

#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <stdlib.h>
#include <string.h>
#include <error.h>
#include <errno.h>
#include <netdb.h>
#include <arpa/inet.h>
#include <unistd.h>

#define BUFF_LEN 32768

int main(int argc, char** argv)
{
    int port = 0;               // listening port
    int sockfd = 0;             // file descriptor
    struct addrinfo hints;
    struct addrinfo* serverinfo;
    int option = 0;
    int n = 0;
    char* filename = "datafile";

    printf("Starting client...\n");

    // Make sure that server name and port number
    // are provided as parameters
    if(argc != 3)
    {
        printf("Server name and port number not provided!\n");
        printf("Usage: %s <servername> <port>\n", argv[0]);
        exit(1);
    }

    // Parse port number from 3rd command line argument
    port = atoi(argv[2]);
    if(port == 0)
    {
        printf("Given port is not a number! (%s)\n", argv[2]);
        exit(1);
    }

    printf("Remote server: %s\n", argv[1]);
    printf("Port number: %d\n", port);

    memset(&hints, 0, sizeof(hints));
    hints.ai_family = AF_INET;		// IPv4
    hints.ai_socktype = SOCK_STREAM;	// TCP socket

    int status = getaddrinfo(argv[1], argv[2], &hints, &serverinfo);
    if(status)
    {
        printf("getaddrinfo error: %s\n", gai_strerror(status));
        exit(1);
    }

    // Print out address
    struct addrinfo *p;
    char ipstr[INET_ADDRSTRLEN];
    for(p=serverinfo; p != NULL; p = p->ai_next)
    {
        void* addr;
        struct sockaddr_in *ip = (struct sockaddr_in*)p->ai_addr;
        addr=&(ip->sin_addr);
        inet_ntop(p->ai_family, addr, ipstr, sizeof(ipstr));
        printf("Address is %s\n", ipstr);
    }

    // Create a TCP socket
    sockfd = socket(serverinfo->ai_family, serverinfo->ai_socktype,
                    serverinfo->ai_protocol);
    if(sockfd < 0)
    {
        printf("Error opening socket (%s)\n", strerror(errno));
        exit(1);
    }

    // Enable address reuse
    option = 1;
    n = setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &option, sizeof(option));
    if(n)
    {
        printf("Error setting socket options! (%s)\n", strerror(errno));
        exit(1);
    }

    // Connect
    int res = connect(sockfd, serverinfo->ai_addr, serverinfo->ai_addrlen);
    if(res)
    {
        printf("Error connecting socket (%s)\n", strerror(errno));
        exit(1);
    }

    // Free up serverinfo linked list
    freeaddrinfo(serverinfo);	// Leaks if exited earlier
    
    char* msg = filename;
    int bytes_sent = send(sockfd, msg, strlen(msg), 0);
    if(bytes_sent == -1)
    {
        printf("Error sending data (%s)\n", strerror(errno));
        exit(1);   
    }
    
    if(bytes_sent != strlen(msg))
    {
        printf("Not all data sent (%d instead of %d)\n",
            bytes_sent, (int)strlen(msg));
    }

    // Save the file that is sent back from the server
    char recbuf[BUFF_LEN];
    memset(recbuf, 0, BUFF_LEN);
    int received = 0;
    int bytes = 0;
    FILE* fp = fopen("received", "w");
    if(!fp)
    {
        printf("Cannot open file (%s)\n", strerror(errno));
        exit(1);
    }
    
    do
    {
        bytes = recv(sockfd, recbuf, BUFF_LEN - 1, 0);
        printf(".");
        fflush(NULL);
        if(bytes < 0)
        {
            printf("Error reading from socket (%s)\n", strerror(errno));
            exit(1);
        }
        // Write received data to file
        fwrite(recbuf, 1, bytes, fp);
    
        received += bytes;
    }while(bytes > 0);
    printf("\ndebug: received %d bytes altogether\n", received);
     
    fclose(fp);

    // Clean up and close
    n = close(sockfd);
    if(n)
    {
        printf("Error closing socket (%s)\n", strerror(errno));
        exit(1);   
    }
    
    printf("Client is done.\n");
    return 0;
}

